import std.concurrency, std.stdio, std.exception, std.variant;
import std.c.time;

//import std.typecons;

import Definitions.msgCatalogue;
import Definitions.ozyConfig;
import Definitions.dspDefs;
import Definitions.procNames;
import Registry.registry;

// In sdrmain.c
extern (C) void dsp_setup();
extern (C) void dsp_tear_down();
// In sdr.c
extern (C) void process_samples(float *, float *, float *, float *, int);
// In update.c
extern (C) int set_rx_filter (int, float, float);
extern (C) int set_mode (int, int, int);
extern (C) float *process_panadapter(int);

extern (C) void msleep(int);

// Main processing loop for the dsp
//===========================================================================================
Tid init_dsp() {
    // A dsp thread
    // Receives raw samples
    // Returns processed samples
    // Provide an interface to get/set the dsp parameters

    auto dsptid = spawn(&dspproc);
    msleep(1000);
    auto pantid = spawn(&panproc);

    return dsptid;
}

void dspproc() {

    // Create the class that will read rx blocks from the device.
    writeln("Creating dsp interface");
    auto odsp = new Dsp();
    odsp.process();
}

void panproc() {

    // Create the class that will periodically transfer pan data.
    writeln("Creating pan process");
    auto opan = new Pan();
    opan.process();
}

//===========================================================================================
class Dsp {

    string mode;
    string filter;

    // Input buffers to dsp process
    float [] left_smpls_buf;
    float [] right_smpls_buf;
    // To collect output samples
    float [] interleave_buf;

    // Constructor
    this() {

        this.left_smpls_buf = new float[512];
        this.right_smpls_buf = new float[512];
        this.interleave_buf = new float[1024];
        // Initialise the dsp
        dsp_setup();

        this.mode = "LSB";
        this.filter = "2K7";
        adjustModeFreq("LSB", "2K7");

    }

    void process() {

        writeln("Dsp processing");
        while (true) {
            receive(
                // Sample dsp processing
                (M_SMPLS_TO_DSP discriminator, immutable (float) [] smpls) {
                    //writeln("Dsp received data ", smpls);
                    //writeln("M_SMPLS_TO_DSP");
                    // Create mutable deinterleaved samples from the immutable interleaved input
                    foreach(i, f; smpls) {
                        if(i%2) {
                            this.left_smpls_buf[i/2] = f;
                        }
                        else {
                            this.right_smpls_buf[i/2] = f;
                        }
                    }
                    process_samples(cast(float *)this.left_smpls_buf, cast(float *)this.right_smpls_buf, cast(float *)(new float[512]), cast(float *)(new float[512]), 512);
                    // Now interleave the results again
                    foreach(j; 0 .. 1023) {
                        if(j%2) {
                            this.interleave_buf[j] = this.left_smpls_buf[j/2];
                        }
                        else {
                            this.interleave_buf[j] = this.right_smpls_buf[j/2];
                        }
                    }

                    immutable(float)[] xfer = this.interleave_buf.idup;
                    CRegistry.getTid(E_PROC.MAIN).send(M_SMPLS_FROM_DSP(), xfer);
                },
                (M_MODE discriminator, string mode) {
                    adjustModeFreq(mode, this.filter);
                },
                (M_RX_FILT discriminator, string filter) {
                    adjustModeFreq(this.mode, filter);
                }
            );
        }
    }

    void adjustModeFreq(string mode, string filter) {

        if(mode <> this.mode) {
            this.mode = mode;
            //writeln("Set mode ", A_MODE[this.mode]);
            set_mode (0, A_MODE[this.mode], TRX_MODE.RX);
            setFilter(filter);
        }
        else if(filter <> this.filter) {
            this.filter = filter;
            setFilter(this.filter);
        }
    }

    void setFilter(string filter) {

        auto filter_low = A_RX_FILT[filter].field[0];
        auto filter_high = A_RX_FILT[filter].field[1];
        auto low = filter_low;
        auto high = filter_high;

        if((this.mode == "USB") || (this.mode == "CWU")) {
            low = -filter_high;
            high = -filter_low;
        }
        else if((this.mode == "AM") || (this.mode == "SAM")) {
            low = -filter_high;
        }

        //writeln("Set filter: ", filter_low, " " , filter_high);
        set_rx_filter (0, low, high);
    }
}

//===========================================================================================
class Pan {

    // Input buffers to pan process
    float *pan_data_buf;
    // Accumulating buffer
    float buf[4096];

    // Constructor
    this() {
    }

    void process() {

        writeln("Pan processing");
        while (true) {
            this.pan_data_buf = process_panadapter(0);
            foreach(i, ref e; buf) {
                e = pan_data_buf[i];
            }
            immutable(float)[] xfer = buf.idup;
            CRegistry.getTid(E_PROC.WEB).send(M_PAN_DATA(), xfer);
            msleep(100);
        }
    }
}
